/**
 * Copyright (C) 2010 Neofonie GmbH
 *
 * This programm is free software; you can redistribute it and/or modify
 * it under the terms of the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package eu.dicodeproject.twitterstream.util;

import com.google.common.base.Preconditions;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Iterator;

/**
 * Provides access to application configuration variables.
 */
public final class Configuration {
  /**Logger for this class.*/
  private static final Logger LOG = LoggerFactory.getLogger(Configuration.class);

  /**Strings that our configuration accepts as keys.*/
  public static final String TWITTER_USERNAME_KEY = "twitter.username";
  public static final String TWITTER_PASSWORD_KEY = "twitter.password";
  public static final String TWEET_RECEIVE_TIMEOUT = "eu.dicodeproject.source.tweetsamplingstream.tweet.receive.timeout";
  public static final int TWEET_DEFAULT_RECEIVE_TIMEOUT = 100;
  public static final String TWEET_TAKE_TIMEOUT = "eu.dicodeproject.source.tweetsamplingstream.tweet.take.timeout";
  public static final int TWEET_DEFAULT_TAKE_TIMEOUT = 10;
  public static final String TWEET_QUEUE_SIZE = "eu.dicodeproject.source.tweetsamplingstream.tweet.queue.size";
  public static final int TWEET_DEFAULT_QUEUE_SIZE = 200;
  public static final String TWEET_ERROR_THRESHOLD = "eu.dicodeproject.source.tweetsamplingstream.tweet.error.threshold";
  public static final int TWEET_DEFAULT_ERROR_THRESHOLD = 300;
  public static final String BROKER_THREAD_POOL_SIZE = "eu.dicodeproject.source.tweetsamplingstream.broker.pool.size";
  public static final int BROKER_DEFAULT_THREAD_POOL_SIZE = 10;
  public static final String HBASE_TABLENAME = "eu.dicodeproject.source.tweetsamplingstream.sink.hbase.tablename";
  public static final String QUERY_BROKER_WORKER_CLASS = "eu.dicodeproject.querybrokerworker.class";
  public static final String QUERY_BROKER_WORKER_PARAMS = "eu.dicodeproject.querybrokerworker.params";

  /**Singleton instance*/
  private static Configuration configurationInstance;
  /**Configuration object*/
  private PropertiesConfiguration configuration;

  /** Make constructor private to ensure singleton property.*/
  private Configuration()  {
    this.configuration = null;

    try {
      this.configuration = new PropertiesConfiguration("dicode.twitter.user.properties");
    } catch (ConfigurationException e) {
      LOG.warn(
          "Loading user configuration dicode.twitter.user.properties failed. Continuing processing with values shipped with initial release. " + e.getMessage());
      LOG.debug("Detailed error message for failed properties loading: ", e);
    }
    try {
      if (this.configuration != null) {
        this.configuration.load("dicode.twitter.properties");
      } else {
        this.configuration = new PropertiesConfiguration("dicode.twitter.properties");
      }
    } catch (ConfigurationException e) {
      LOG.warn(
          "Loading configuration dicode.twitter.properties failed. Continuing processing with default values."
          + e.getMessage());
      LOG.debug("Detailed error message: ", e);
      this.configuration = null;
    }

    if (LOG.isDebugEnabled()) {
      @SuppressWarnings("unchecked")
      Iterator<String> keyIter = this.configuration.getKeys();
      StringBuilder builder = new StringBuilder();
      while (keyIter.hasNext()) {
        String key = keyIter.next();
        builder.append(key).append(":").append(this.configuration.getString(key)).append("; ");
      }
      LOG.debug("Effective properties: " + builder.toString());
    }
  }

  /**
   * Returns the single instance of this configuration object.
   * */
  public static synchronized Configuration getInstance() {
    if (configurationInstance == null) {
      configurationInstance = new Configuration();
    }
    return configurationInstance;
  }

  /**
   * Retrieve a configuration value, returns the default value, if non was found in the config file.
   * @param key the configuration key
   * @param defaultValue the default value to fall back to
   * @return defaultValue if no value matching key was found, otherwise the value found in the configuration object.
   * */
  public int getIntValue(String key, int defaultValue) {
    if (configuration != null) {
      return configuration.getInt(key, defaultValue);
    } else {
      return defaultValue;
    }
  }

  /**
   * Retrieve a configuration value, returns the default value if non was found in the config file.
   * @param key the configuration key
   * @param defaultValue the default value to fall back to
   * @return the configured value matching the key, defaultValue otherwise
   * */
  public String getStringValue(String key, String defaultValue) {
    if (configuration != null) {
      return configuration.getString(key, defaultValue);
    } else {
      return defaultValue;
    }
  }

  /**
   * Get the string associated with the given key.
   * @return string associated with the key.
   * @throws NullPointerException in case there is no object associated with this key.
   * */
  public String getStringValue(String key) {
    Preconditions.checkNotNull(configuration);
    Preconditions.checkNotNull(configuration.getString(key));
    return configuration.getString(key);
  }
}
